Refactor temperature reading, improve error handling, and optimize CPU usage - #3
Merged
Conversation
macmon's temp.cpu_temp_avg is not trustworthy. Measured inside a SINGLE
invocation with CPU load flat at 1.5-3.7% and cpu_power at 0.06W:
t+0s cpu=37.74 gpu=35.33
t+2s cpu=31.79 gpu=35.27
t+4s cpu=20.19 gpu=35.27
A die cannot cool 17C in two seconds and reheat. Across trials the value
is bimodal, landing on ~31.9 or ~38.1 and never between, which is the
signature of a mean over a varying sensor set rather than a temperature.
helpers/thermal.swift reads the PMU tdie sensors directly through
IOHIDEventSystemClient (no sudo). Spread 0.30C over 20 reads, and it
tracks real load: 34.9 idle -> 37.6 under full load, monotonic.
The helper was untracked until now, while both install and system.sh
depended on it. Both guarded defensively, so a fresh clone produced a bar
with no temperature and no error at all.
The two helper build blocks tested `[[ -r $src ]] && command -v swiftc` in one condition, so a MISSING SOURCE and a MISSING TOOLCHAIN were indistinguishable and both exited without printing anything -- no ok, no warn. thermal.swift was untracked in git, so a fresh clone hit exactly that path and produced a bar with no temperature and no explanation. Replace both with one build_sb_helper function carrying a three-way guard, so each outcome reports itself.
…P errors sketchybarrc pointed at "the privacy note in weather.sh"; no such note existed. With LOCATION empty, wttr.in geolocates by SOURCE IP, so every poll discloses the host's public IP to a third party. That is a fair trade for zero configuration but it is not visible from the URL. Write the note, and add SKETCHYBAR_WEATHER_LOCATION so the behaviour can be pinned to a place or turned off. Also curl -sf: without -f, curl exits 0 on a 5xx and the error page is written to the cache, which is then served as the last-known-good reading indefinitely.
The rule was commented out on the belief that Raycast was in the Brewfile but not installed. /Applications/Raycast.app exists and its Info.plist gives com.raycast.macos -- exactly the id the comment declined to guess. Caveat recorded inline: the id came from Info.plist, not from `aerospace list-apps` with the Settings window open, since Raycast was not running.
volume showed "missing value%". osascript prints that literal string and exits 0 for a device with no software volume, so the `[ -z "$vol" ]` guard sailed past it. The output here is a Focusrite Scarlett 2i2, which exposes neither kAudioDevicePropertyVolumeScalar nor Mute on any scope or element -- confirmed against CoreAudio. There is genuinely no level to show. brightness showed a constant 100%. It looped display ids 1..16 and took the first DisplayServicesGetBrightness that answered; on this clamshell desk that is the external, and the call SUCCEEDS for a non-Apple external and returns a hardcoded 1.0. The 100% was fabricated, not stale. Both now read the right source (brightness: gamma ramp for an external, which is what MonitorControl's software dimming manipulates -- verified 0.75/0.50/0.25 -> 75/50/25%) and hide rather than invent a number. colors.sh gains the compositing note left over from a frosted-bar experiment that was reverted.
Three failure classes that are invisible when they occur, all found the
hard way:
- a self-hiding item under the config-wide updates=when_shown default
stops being updated entirely, so it can never un-hide. Eight items
were affected; each worked after a reload and then went quiet.
- SketchyBar fork_execs plugins and reports NOTHING when the execute
bit is missing. The item simply never updates.
- a plugin runs with less TCC access than a terminal, so ~/Library
reads succeed by hand and fail with EPERM in the bar, at correct
Unix permissions.
docs/audit-2026-08-02.md is a full production-readiness review with 20
findings, measured costs per plugin, and a four-phase roadmap.
…d data mic was polled every 2s on the strength of a comment in mic.swift saying the helper "runs in single-digit ms". Measured min/median/max over 10 consecutive runs: 60/63/65ms -- wrong by a factor of 8. At 2s that item alone cost 1890ms of CPU per MINUTE, 36% of the whole bar's budget and the single most expensive thing in the config. Now 5s. The cost is inherent, not a Swift artefact: the helper enumerates every CoreAudio device and queries two properties each, so it is O(devices) IPC into coreaudiod per tick. Making it free means a property listener, not a faster loop; noted in the source. system was 30s at ~930ms per invocation (macmon alone measured 854ms, because it samples a full SoC telemetry frame to extract three numbers). Now 60s. CPU%, RAM and fan RPM are not acted on faster than that. Together: 3750 -> 1686 ms/min, a 55% reduction in the bar's idle CPU.
Each of the nine workspace items ran its own copy of aerospace.sh, and each copy shelled out to `aerospace list-workspaces` to ask a GLOBAL question -- which workspaces hold windows -- whose answer is identical for all nine. Measured 25ms per call, 104ms for the nine, plus nine greps. Worse, the items were subscribed to front_app_switched as well as aerospace_workspace_change, so all of that ran on every application switch, one of the highest-frequency actions there is. Replace with a zero-width driver item that owns the subscriptions and rewrites every pill in a single sketchybar call: 2 aerospace calls and 1 sketchybar call regardless of workspace count. Full repaint now 54ms. The occupancy test is parameter expansion instead of a grep per item, with sentinel spaces so a future workspace 10 cannot match workspace 1.
Both PATH exports hardcoded /opt/homebrew, the Apple Silicon prefix. On an Intel Mac every plugin depending on aerospace, macmon, gh or icalBuddy would fail to find its binary -- and they all hide rather than error when that happens, so the bar would come up merely looking quiet. Prepending a directory that does not exist is harmless, and this avoids a `brew --prefix` subprocess on every bar start.
hide() was defined identically in three plugins, the truncate idiom was
copy-pasted into three more, the helper build-on-demand block existed
twice, and amphetamine.sh had a six-line osascript block duplicated
verbatim inside itself.
lib.sh provides hide, truncate_label, state_file, ensure_helper and
require. Notes on two of them:
hide() now closes any popup unconditionally. Two of the three local
definitions did that and the third has no popup; popup.drawing=off on a
popup-less item is accepted silently (verified). Doing it always removes
a footgun -- a bare hide() that left a popup on screen would be a rare
and subtle bug.
truncate_label replaces `printf | cut -c1-N`, a two-subprocess pipeline
run on every invocation of every plugin that shows a name. cut -c counts
BYTES in some implementations while ${#s} and ${s:0:n} count characters,
so the old form could cut a UTF-8 name mid-codepoint.
require() gives missing dependencies a voice: plugins previously failed
into empty output and then hid, which is indistinguishable from "nothing
to report".
Found by scripts/lint-sketchybar.sh, not by the hand audit that claimed to have found all of them. music.sh hides via a hide() call rather than a literal `--set "$NAME" drawing=off`, so the grep used to sweep for this never saw it. The item was live-broken: Apple Music is usually not running, so the item was sitting hidden with updates=when_shown, meaning starting playback would never have brought the pill back.
Three SketchyBar behaviours produce no error and no log line, so the only
symptom is an item that quietly stops updating:
- a plugin without its execute bit; sketchybar fork_execs it and says
nothing
- a self-hiding item without updates=on, which can then never un-hide
- a comment referencing a section that no longer exists, in a config
where the comments carry the measurements
scripts/lint-sketchybar.sh checks all three, and earned its place
immediately by finding the music item bug above.
It is deliberately precise about two things it must NOT flag:
label.drawing=off and background.drawing=off hide a COMPONENT, leaving the
item drawn and updating (amphetamine and pomodoro both do this correctly);
and passive items like cpu and memory carry no script of their own, so
they are un-hidden by whichever item's script writes them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces several improvements and clarifications to the repository's documentation, configuration, and automation. The most significant changes are the addition of a shell linting workflow, expanded and reorganized documentation for contributors (especially for Claude Code), and updates to the AeroSpace configuration and related documentation to clarify keybindings and window gap logic.
Automation and Linting
.github/workflows/lint.ymlto automate shell script linting withshellcheck, parse theinstallscript with zsh, and check SketchyBar configuration semantics. This helps catch errors early and enforce code quality for shell scripts.shellcheckto theBrewfilefor local linting support.Documentation and Contributor Guidance
CLAUDE.md:.zshrcload order. Also clarified theme and vendored script requirements.README.mdto clarify that AeroSpace usesctrl-1–ctrl-9for workspace switching (notalt), with rationale for avoidingaltdue to keyboard layout conflicts.Configuration Updates
aerospace/.config/aerospace/aerospace.toml):gaps.outer.topto a fixed value (43) to match the SketchyBar height and simplify per-monitor gap logic, removing previous per-monitor handling.Package Management
media-controlto theBrewfilefor improved media key support.These changes enhance code quality, onboarding clarity, and configuration maintainability.